﻿using System;
using System.Collections.Generic;
using SymbolIntegration.core.Helpers;

namespace SymbolIntegration.core.Expressions.Functions
{
    public class LnFunction : FuncExpression
    {
        internal static FuncExpression ParseItself(string expression, int position, int size)
        {
            var result = new LnFunction();
            result.InnerParseItself(expression, position, size);
            return result;
        }

        protected LnFunction()
        {

        }

        public LnFunction(Expression expr)
            : base(expr)
        {

        }

        public override Expression Clone()
        {
            var clone = new LnFunction(m_Argument);
            InnerCopy(clone);
            return clone;
        }

        public override Expression Normalize()
        {
            Expression result;
            do
            {
                var normalArg = m_Argument.Normalize();

                if (m_Argument.Type == ExpressionType.Power && normalArg.Type == ExpressionType.Addends)
                {
                    var power = m_Argument as PowerExpression;
                    result = power.Power * new LnFunction(power.Base);
                    result = result.InvertNegate(IsNegate ^ power.IsNegate).Normalize();
                    break;
                }

                if (m_Argument.Type == ExpressionType.Multipliers && normalArg.Type == ExpressionType.Addends)
                {
                    var mults = m_Argument as MultipliersExpression;
                    result = new ConstantExpression(0);
                    foreach (var multiplier in mults.Multipliers)
                    {
                        result += new LnFunction(multiplier);
                    }
                    result += new LnFunction(mults.Constant.InvertNegate(mults.IsNegate));
                    result = result.InvertNegate(IsNegate).Normalize();
                    break;
                }

                var normalConst = normalArg as ConstantExpression;
                if (normalConst != null)
                {
                    if (normalConst.Value > 0)
                    {
                        result = new ConstantExpression(Math.Log(normalConst.Value));
                        break;
                    }
                }


                if (normalArg.Type == ExpressionType.Power)
                {
                    var power = normalArg as PowerExpression;
                    result = power.Power * new LnFunction(power.Base);
                    result = result.InvertNegate(IsNegate ^ power.IsNegate).Normalize();
                    break;
                }

                if (normalArg.Type == ExpressionType.Multipliers)
                {
                    var mults = normalArg as MultipliersExpression;
                    result = new ConstantExpression(0);
                    foreach (var multiplier in mults.Multipliers)
                    {
                        result += new LnFunction(multiplier);
                    }
                    result += new LnFunction(mults.Constant.InvertNegate(mults.IsNegate));
                    result = result.InvertNegate(IsNegate).Normalize();
                    break;
                }

                var lnResult = new LnFunction(normalArg);
                InnerCopy(lnResult);
                result = lnResult;
            }
            while (false);

            return result;
        }

        protected internal override Expression IntegrateAsDependentFromVar(string variable, IIntegrator integrator)
        {
            Expression result;
            do
            {
                var polinomArgument = m_Argument.FormatToPolinom(variable);
                if (polinomArgument != null && polinomArgument.GetMaxPower() == 1)
                {
                    result = Argument * this / polinomArgument.GetConstantAt(1);
                    result = result - Argument/polinomArgument.GetConstantAt(1);
                    break;
                }

                result = integrator.TryIntegrateMultipliers(new List<Expression> { this }, variable);
            } 
            while (false);

            return result;
        }

        public override FunctionType FunctionType
        {
            get { return FunctionType.Ln; }
        }

        protected override string GetFunctionName()
        {
            return "ln";
        }

        protected override ConstantExpression Apply(double constant)
        {
            return new ConstantExpression(Math.Log(constant));
        }

        protected override Expression InnerFuncDifferentiate(string argument)
        {
            var result = new ConstantExpression(1)/m_Argument;
            return result;
        }
    }
}